#include <stdio.h>
#include "msg/msg.h"            /* Yeah! If you want to use msg, you need to include msg/msg.h */
#include "xbt/sysdep.h"         /* calloc, printf */
/* Create a log channel to have nice outputs. */
#include "xbt/log.h"
#include "xbt/asserts.h"


XBT_LOG_NEW_DEFAULT_CATEGORY(MRS, "Messages specific for this msg example");

double minTime = 0, maxTime = 0;

const char *fastestHost, *slowestHost;

int master(int argc, char *argv[]);
int slave(int argc, char *argv[]);
int forwarder(int argc, char *argv[]);
msg_error_t test_all(const char *platform_file, const char *application_file);
#define FINALIZE ((void*)221297)        /* a magic number to tell people to stop working */


int master(int argc, char *argv[]) {
	int slaves_count = 0;
	msg_host_t *slaves = NULL;
	msg_task_t *todo = NULL;
	int number_of_tasks = 0;
	double task_comp_size = 0;
	double task_comm_size = 0;
	int i;

	for (i = 1; i < argc; i++)
	{
		XBT_INFO("Master arguments %d: %s", i, argv[i]);
	}
	_XBT_GNUC_UNUSED int res = sscanf(argv[1], "%d", &number_of_tasks);
	xbt_assert(res, "Invalid argument %s\n", argv[1]);
	res = sscanf(argv[2], "%lg", &task_comp_size);
	xbt_assert(res, "Invalid argument %s\n", argv[2]);
	res = sscanf(argv[3], "%lg", &task_comm_size);
	xbt_assert(res, "Invalid argument %s\n", argv[3]);
	{ /*  Task creation */
		char sprintf_buffer[64];
		todo = xbt_new0(msg_task_t, number_of_tasks);
		for (i = 0; i < number_of_tasks; i++) {
			sprintf(sprintf_buffer, "Task_%d", i);
			todo[i] = MSG_task_create(sprintf_buffer, task_comp_size,
					task_comm_size,
					NULL);
		}
	}
	{ /* Process organisation */
		slaves_count = argc - 4;
		slaves = xbt_new0(msg_host_t, slaves_count);
		for (i = 4; i < argc; i++) {
			slaves[i - 4] = MSG_get_host_by_name(argv[i]);
			xbt_assert(slaves[i - 4] != NULL, "Unknown host %s. Stopping Now! ",
					argv[i]);
		}
	}
	XBT_INFO("Got %d slaves and %d tasks to process", slaves_count,
			number_of_tasks);
	for (i = 0; i < slaves_count; i++)
		XBT_DEBUG("%s", MSG_host_get_name(slaves[i]));
	for (i = 0; i < number_of_tasks; i++) {
		XBT_INFO("Sending \"%s\" to \"%s\"", todo[i]->name,
				MSG_host_get_name(slaves[i % slaves_count]));
		if (MSG_host_self() == slaves[i % slaves_count]) {
			XBT_INFO("Hey ! It's me ! :)");
		}
		MSG_task_send(todo[i], MSG_host_get_name(slaves[i % slaves_count]));
		XBT_INFO("Sent");
	}
	XBT_INFO(
			"All tasks have been dispatched. Let's tell everybody the computation is over.");
	for (i = 0; i < slaves_count; i++) {
		msg_task_t finalize = MSG_task_create("finalize", 0, 0, FINALIZE);
		MSG_task_send(finalize, MSG_host_get_name(slaves[i]));
	}
	XBT_INFO("Goodbye now!");
	free(slaves);
	free(todo);
	return 0;
}

int slave(int argc, char *argv[]) {
	msg_task_t task = NULL;
	double startTime, runningTime;
	_XBT_GNUC_UNUSED int res;
	while (1) {
		res = MSG_task_receive(&(task), MSG_host_get_name(MSG_host_self()));
		xbt_assert(res == MSG_OK, "MSG_task_get failed");
		XBT_INFO("Received \"%s\"", MSG_task_get_name(task));
		if (!strcmp(MSG_task_get_name(task), "finalize")) {
			MSG_task_destroy(task);
			break;
		}
		startTime = MSG_get_clock();
		XBT_INFO("Processing \"%s\"", MSG_task_get_name(task));
		XBT_INFO("The computation time estimated %lf", MSG_task_get_remaining_computation(task));
		MSG_task_execute(task);

		XBT_INFO("\"%s\" done", MSG_task_get_name(task));
		runningTime = MSG_get_clock() - startTime;
//		XBT_INFO("Running time of the task %s was %lf on the node %s", MSG_task_get_name(task), runningTime, MSG_host_get_name(MSG_host_self()));

		if (maxTime == 0)
		{
			minTime = runningTime;
			maxTime = minTime;
			slowestHost = MSG_host_get_name(MSG_host_self());
			fastestHost = MSG_host_get_name(MSG_host_self());
		}
		else
		{
			if (runningTime >= maxTime)
			{
				maxTime = runningTime;
				slowestHost = MSG_host_get_name(MSG_host_self());
			}
			else if (runningTime < minTime)
			{
				minTime = runningTime;
				fastestHost = MSG_host_get_name(MSG_host_self());
			}
		}

		MSG_task_destroy(task);
		task = NULL;
	}
	XBT_INFO("I'm done. See you!");
	return 0;
}


msg_error_t test_all(const char *platform_file, const char *application_file) {
	msg_error_t res = MSG_OK;
	/* MSG_config("workstation/model","KCCFLN05"); */
	{ /*  Simulation setting */
		MSG_create_environment(platform_file);
	}
	{ /*   Application deployment */
		MSG_function_register("master", master);
		MSG_function_register("worker", slave);
		MSG_launch_application(application_file);
	}
	res = MSG_main();
	XBT_INFO("Simulation time %lf", MSG_get_clock());
	return res;
}

int main(int argc, char *argv[]) {
	msg_error_t res = MSG_OK;
	MSG_init(&argc, argv);
	if (argc < 3) {
		printf("Usage: %s platform_file deployment_file\n", argv[0]);
		printf("example: %s msg_platform.xml msg_deployment.xml\n", argv[0]);
		exit(1);
	}
	res = test_all(argv[1], argv[2]);
	if (res == MSG_OK)
	{
		XBT_INFO("The fastest task was running within %lf seconds in node %s", minTime, fastestHost);
		XBT_INFO("The slowest task was running within %lf seconds in node %s", maxTime, slowestHost);
		return 0;
	}
	else
		return 1;
}
